package com.thyc.server;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.thyc.entity.InstructionsSent;
import com.thyc.entity.SysConfig;
import com.thyc.service.InstructionsSentService;
import com.thyc.service.SysConfigService;
import com.thyc.utils.JkConvertUtil;
import com.thyc.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 6912000万数据每天   50hz
 */
@Component
@Slf4j
public class SocketServer {

    private static final String ENABLE_STATUS = "^24([3][0|8]){16}$";

    private static final String CHANNEL_LIST = "CHANNEL_LIST";

    private static final String SOURCE_DATA = "SOURCE_DATA";

    @Resource
    private InstructionsSentService instructionsSentService;

    @Resource
    SysConfigService sysConfigService;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private StartComponent startComponent;

    @Scheduled(fixedDelay = 1000 * 60 * 30)
    public void startSocketServer() {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream inputStream;
        OutputStream outputStream;
        DataInputStream dis;
        try {
            serverSocket = new ServerSocket(8051);
            socket = serverSocket.accept();
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
            dis = new DataInputStream(inputStream);
            Date now = new Date();
            log.info("连接到客户端：{},时间：{}", socket.getInetAddress(), DateUtil.format(now, "yyyy-MM-dd HH:mm:ss"));
            startComponent.run();
            byte[] bytes = new byte[1];
            Boolean flag = true;
            StringBuffer sb = new StringBuffer();
            List<String> list = new ArrayList<>();
            //如果redis有通道数据 就不解析设备开启的通道
            Object enableChannels = redisUtil.get(CHANNEL_LIST);
            if (ObjectUtil.isNotEmpty(enableChannels)) {
                list = (List<String>) enableChannels;
            }
            while (dis.read(bytes) != -1) {
                sb.append(JkConvertUtil.bytesToHexString(bytes).replace(" ", ""));
                //符合正则，则表示收到消息头部信息，用来查看当前开启的通道有哪些
                if (ReUtil.count(ENABLE_STATUS, sb) > 0) {
                    if (ObjectUtil.isEmpty(list)) {
                        String string = JkConvertUtil.convertHexToString(sb.toString());
                        list = JkConvertUtil.transformationToMap(string);
                        redisUtil.set(CHANNEL_LIST, list);
                    }
                    sb.delete(0, sb.length());
                }
                //如果符合条件，则表示收到一组完整的数据包，其中包含每组通道的数据
                if (ObjectUtil.isNotEmpty(list) && sb.length() == (list.size() * 4) + 4 && sb.toString().startsWith("0000")) {
                    List<Integer> dataList = JkConvertUtil.hexToDecimal(StrUtil.removePrefix(sb, "0000"));
                    //直接把解析后的10进制数据存到缓存中 暂不带入公式计算
                    redisUtil.lSet(SOURCE_DATA, dataList);
                    sb.delete(0, sb.length());
                    //如果采集时间够3分钟 关闭流
                    if (DateUtil.between(new Date(), now, DateUnit.MINUTE) >= 3) {
                        dis.close();
                        outputStream.close();
                        inputStream.close();
                        break;
                    }
                }
                //如果是心跳 发送采集数据指令
                if (sb.toString().contains("4554554E4700")) {
                    if (flag) {
                        sendInstructions(outputStream, "#06200035f!");
                        flag = false;
                    }
                    sb.delete(0, sb.length());
                }
            }
        } catch (Exception e) {
            log.warn(e.getMessage());
            //更新dto错误连接次数
            Integer configValue = Integer.valueOf(sysConfigService.getOne(new LambdaQueryWrapper<SysConfig>().eq(SysConfig::getConfigKey, "jk.dtu.error.num")).getConfigValue()) + 1;
            sysConfigService.update(new LambdaUpdateWrapper<SysConfig>().eq(SysConfig::getConfigKey, "jk.dtu.error.num")
                    .set(SysConfig::getConfigValue, configValue));
            try {
                socket.close();
                serverSocket.close();
            } catch (IOException ex) {
                log.warn("socket+serverSocket"+ex.getMessage());
                ex.printStackTrace();
            }
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    log.warn("socket"+e.getMessage());
                    e.printStackTrace();
                }
            }
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    log.warn("serverSocket"+e.getMessage());
                    e.printStackTrace();
                }
            }
        }
        log.info("三分钟数据采集完成");
    }

    void sendInstructions(OutputStream outputStream, String instructions) throws IOException {
        PrintStream dataOutputStream = new PrintStream(outputStream);
        dataOutputStream.write(instructions.getBytes());
        dataOutputStream.flush();
        log.info("发送指令===========================》{}", instructions);
        saveInstructionsSent(instructions);
    }

    void saveInstructionsSent(String instructions) {
        InstructionsSent instructionsSent = new InstructionsSent();
        instructionsSent.setModelNum("06200035");
        instructionsSent.setInstruction(instructions);
        instructionsSentService.save(instructionsSent);
    }

}
